(ns intro.visualization
(:require [scicloj.kind-clerk.api :as kind-clerk]
[tablecloth.api :as tc]
[aerial.hanami.common :as hc]
[aerial.hanami.templates :as ht]
[scicloj.noj.v1.vis.hanami.templates :as vht]
[scicloj.noj.v1.vis :as vis]
[scicloj.noj.v1.stats :as stats]
[tech.v3.datatype :as dtype]
[tech.v3.datatype.functional :as fun]
[scicloj.kindly.v3.api :as kindly]
[scicloj.kindly.v3.kind :as kind]
[hiccup.core :as hiccup]
[clojure2d.color :as color]))(-> "Hello, Noj.
"
vis/raw-html)(-> [:svg {:height 210
:width 500}
[:line {:x1 0
:y1 0
:x2 200
:y2 200
:style "stroke:rgb(255,0,0);stroke-width:2"}]]
hiccup/html
vis/raw-html)Noj offers a few convenience functions to make Hanami plotting work smoothly with Tablecloth and Kindly.
(def mtcars
(-> "data/mtcars.csv"
(tc/dataset {:key-fn keyword})))(def iris
(-> "data/iris.csv"
(tc/dataset {:key-fn keyword})))(def random-walk
(let [n 20]
(-> {:x (range n)
:y (->> (repeatedly n #(- (rand) 0.5))
(reductions +))}
tc/dataset)))We can plot a Tablecloth datasete using a Hanami template:
(-> random-walk
(vis/hanami-plot ht/point-chart
{:MSIZE 200}))Let us look inside the resulting vega-lite space. We can see the dataset is included as CSV:
(-> random-walk
(vis/hanami-plot ht/point-chart
{:MSIZE 200})
kind/pprint){:encoding
{:y {:field "y", :type "quantitative"},
:x {:field "x", :type "quantitative"}},
:mark {:type "circle", :size 200, :tooltip true},
:width 400,
:background "floralwhite",
:height 300,
:data
{:values
"x,y\n0,0.16318180423603845\n1,0.18081929755683057\n2,0.24463929196632306\n3,0.024996107334412487\n4,-0.19693526724400334\n5,-0.5989939541990973\n6,-0.9151308758590203\n7,-0.4694369711767141\n8,-0.8239323254632093\n9,-0.36566137066270077\n10,-0.8005472393015366\n11,-0.9280491652487008\n12,-0.5140021041644375\n13,-0.8535438387330152\n14,-0.6687297877707352\n15,-0.2651390714139542\n16,-0.28587633824170966\n17,-0.38913318838472977\n18,-0.6221157004219788\n19,-0.5384114424404509\n",
:format {:type "csv"}}}
The scicloj.noj.v1.vis.hanami.templates namespace add Hanami templates to Hanami's own collection.
(-> mtcars
(vis/hanami-plot vht/boxplot-chart
{:X :gear
:XTYPE :nominal
:Y :mpg}))(-> random-walk
(vis/hanami-layers
{:TITLE "points and a line"}
[(vis/hanami-plot nil
ht/point-chart
{:MSIZE 400})
(vis/hanami-plot nil
ht/line-chart
{:MSIZE 4
:MCOLOR "brown"})]))(-> random-walk
(vis/hanami-vconcat
{}
[(vis/hanami-plot nil
ht/point-chart
{:MSIZE 400
:HEIGHT 100
:WIDTH 100})
(vis/hanami-plot nil
ht/line-chart
{:MSIZE 4
:MCOLOR "brown"
:HEIGHT 100
:WIDTH 100})]))(-> random-walk
(vis/hanami-hconcat
{}
[(vis/hanami-plot nil
ht/point-chart
{:MSIZE 400
:HEIGHT 100
:WIDTH 100})
(vis/hanami-plot nil
ht/line-chart
{:MSIZE 4
:MCOLOR "brown"
:HEIGHT 100
:WIDTH 100})]))(-> mtcars
(stats/add-predictions :mpg [:wt]
{:model-type :smile.regression/ordinary-least-square})
(vis/hanami-layers {}
[(vis/hanami-plot nil
ht/point-chart
{:X :wt
:Y :mpg
:MSIZE 200
:HEIGHT 200
:WIDTH 200})
(vis/hanami-plot nil
ht/line-chart
{:X :wt
:Y :mpg-prediction
:MSIZE 5
:MCOLOR "purple"
:YTITLE :mpg})]))(-> iris
(vis/hanami-histogram :sepal-width
{:nbins 10}))The following is inspired by the example at Plotnine's main page. Note how we add regression lines here. We take care of layout and colouring on our side, not using Vega-Lite for that.
(let [pallete (->> :accent
color/palette
(mapv color/format-hex))]
(-> mtcars
(tc/group-by :gear {:result-type :as-map})
(->> (sort-by key)
(map-indexed
(fn [i [group-name ds]]
(-> ds
(stats/add-predictions :mpg [:wt]
{:model-type :smile.regression/ordinary-least-square})
(tc/select-columns [:gear :wt :mpg :mpg-prediction])
(vis/hanami-layers {:TITLE (str "grear=" group-name)}
[(vis/hanami-plot nil
ht/point-chart
{:X :wt
:Y :mpg
:MSIZE 200
:MCOLOR (pallete i)
:HEIGHT 200
:WIDTH 200})
(vis/hanami-plot nil
ht/line-chart
{:X :wt
:Y :mpg-prediction
:MSIZE 5
:MCOLOR (pallete i)
:YTITLE :mpg})]
))))
(vis/hanami-vconcat nil {}))))A similar example with histograms:
(let [pallete (->> :accent
color/palette
(mapv color/format-hex))]
(-> iris
(tc/group-by :species {:result-type :as-map})
(->> (sort-by key)
(map-indexed
(fn [i [group-name ds]]
(-> ds
(vis/hanami-histogram :sepal-width
{:nbins 10}))))
(vis/hanami-vconcat nil {}))))Scatterplots and regression lines again, this time using Vega-Lite for layout and coloring (using its "facet" option).
(-> mtcars
(tc/group-by [:gear])
(stats/add-predictions :mpg [:wt]
{:model-type :smile.regression/ordinary-least-square})
(tc/ungroup)
(tc/select-columns [:gear :wt :mpg :mpg-prediction])
(vis/hanami-layers {}
[(vis/hanami-plot nil
ht/point-chart
{:X :wt
:Y :mpg
:MSIZE 200
:COLOR "gear"
:HEIGHT 100
:WIDTH 200})
(vis/hanami-plot nil
ht/line-chart
{:X :wt
:Y :mpg-prediction
:MSIZE 5
:COLOR "gear"
:YTITLE :mpg})])
((fn [spec]
{:facet {:row {:field "gear"}}
:spec (dissoc spec :data)
:data (:data spec)}))
kind/vega-lite):bye:bye